/*
日志器模块：
  1.抽象日志器模块
  2.派生成不同的子类（同步日志器 || 异步日志器）
*/
#ifndef __M_LOGGER_H__
#define __M_LOGGER_H__
// #define _GUN_SOURCE
#include "util.hpp"
#include "level.hpp"
#include "format.hpp"
#include "sink.hpp"
#include <atomic>
#include <mutex>
#include <cstdarg> // 推荐在 C++ 中使用
#include <cassert>
#include "lopper.hpp"
#include <unordered_map>
namespace xglog
{
  class Logger
  {
  public:
    using ptr = std::shared_ptr<Logger>;
    Logger(const std::string &logger_name, LogLevel::value level, Formater::ptr &formater, std::vector<LogSink::ptr> &sinks)
        : _logger_name(logger_name), _limit_level(level), _formater(formater), _sinks(sinks.begin(), sinks.end()) {}
    /*完成构造日志消息对象过程并进行格式化，得到格式化后的日志消息字符串 ----然后进行落地输出*/ // 就是构造msg
    const std::string &getName()
    {
      return _logger_name;
    }
    void debug(const std::string &file, size_t line, const std::string &fmt, ...)
    {
      // 通过传入的参数构造出一个日志消息对象，进行日志格式化，最终落地
      // 1.判断当前的日志是否达到了输出等级
      if (LogLevel::value::DEBUG < _limit_level)
        return;
      //  "这是一个测试日志，值为 %d", 42比如这种
      // 2.对fmt格式化字符串和不定参进行字符串组织，得到的日志消息的字符串.数据放在res中
      va_list ap; // 可变参数列表的变量
      va_start(ap, fmt);
      char *res;
      int ret = vasprintf(&res, fmt.c_str(), ap);
      if (ret == -1)
      {
        std::cout << "vasprintf failed!!\n";
        return;
      }
      va_end(ap);
      // 对得到的日志消息字符串进行格式化整理然后输出到不同位置
      serialize(LogLevel::value::DEBUG, file, line, res);
      free(res);
    }
    void info(const std::string &file, size_t line, const std::string &fmt, ...)
    {
      // 通过传入的参数构造出一个日志消息对象，进行日志格式化，最终落地
      // 1.判断当前的日志是否达到了输出等级
      if (LogLevel::value::INFO < _limit_level)
        return;
      // 2.对fmt格式化字符串和不定参进行字符串组织，得到的日志消息的字符串
      va_list ap;
      va_start(ap, fmt);
      char *res;
      int ret = vasprintf(&res, fmt.c_str(), ap);
      if (ret == -1)
      {
        std::cout << "vasprintf failed!!\n";
        return;
      }
      va_end(ap);
      serialize(LogLevel::value::INFO, file, line, res);
      free(res);
    }
    void warn(const std::string &file, size_t line, const std::string &fmt, ...)
    {
      // 通过传入的参数构造出一个日志消息对象，进行日志格式化，最终落地
      // 1.判断当前的日志是否达到了输出等级
      if (LogLevel::value::WARN < _limit_level)
        return;
      // 2.对fmt格式化字符串和不定参进行字符串组织，得到的日志消息的字符串
      va_list ap;
      va_start(ap, fmt);
      char *res;
      int ret = vasprintf(&res, fmt.c_str(), ap);
      if (ret == -1)
      {
        std::cout << "vasprintf failed!!\n";
        return;
      }
      va_end(ap);
      serialize(LogLevel::value::WARN, file, line, res);
      free(res);
    }
    void error(const std::string &file, size_t line, const std::string &fmt, ...)
    {
      // 通过传入的参数构造出一个日志消息对象，进行日志格式化，最终落地
      // 1.判断当前的日志是否达到了输出等级
      if (LogLevel::value::ERROR < _limit_level)
        return;
      // 2.对fmt格式化字符串和不定参进行字符串组织，得到的日志消息的字符串
      va_list ap;
      va_start(ap, fmt);
      char *res;
      int ret = vasprintf(&res, fmt.c_str(), ap);
      if (ret == -1)
      {
        std::cout << "vasprintf failed!!\n";
        return;
      }
      va_end(ap);
      serialize(LogLevel::value::ERROR, file, line, res);
      free(res);
    }
    void fatal(const std::string &file, size_t line, const std::string &fmt, ...)
    {
      // 通过传入的参数构造出一个日志消息对象，进行日志格式化，最终落地
      // 1.判断当前的日志是否达到了输出等级
      if (LogLevel::value::FATAL < _limit_level)
        return;
      // 2.对fmt格式化字符串和不定参进行字符串组织，得到的日志消息的字符串
      va_list ap;
      va_start(ap, fmt);
      char *res;
      int ret = vasprintf(&res, fmt.c_str(), ap);
      if (ret == -1)
      {
        std::cout << "vasprintf failed!!\n";
        return;
      }
      va_end(ap);
      serialize(LogLevel::value::FATAL, file, line, res);
      free(res);
    }

  protected:
    /* 抽象接口完成实际的落地输出，不同的日志器会有不同的实际落地方式 */
    virtual void log(const char *data, size_t len) = 0;
    void serialize(LogLevel::value level, const std::string &file, size_t line, char *str)
    {
      // 3.构造logMsg对象，将传进来的参数构造成具体消息，然后再格式化
      LogMsg msg(level, line, file, _logger_name, str);
      // 4.通过格式化工具对LogMsg进行格式化，得到格式化后的日志字符串
      std::stringstream ss;
      _formater->format(ss, msg);
      // 5.进行日志落地
      log(ss.str().c_str(), ss.str().size()); // 只是传递参数，具体是谁来实现，看是哪个对象调用
    }

  protected:
    std::mutex _mutex;
    std::string _logger_name;
    std::atomic<LogLevel::value> _limit_level; // 限制等级
    Formater::ptr _formater;                   // 输出格式
    std::vector<LogSink::ptr> _sinks;          // 落地位置
  };
  // 同步日志器
  class SyncLogger : public Logger
  {
  public:
    SyncLogger(const std::string &logger_name, LogLevel::value level, Formater::ptr &formater, std::vector<LogSink::ptr> &sinks)
        : Logger(logger_name, level, formater, sinks) {}

  protected:
    void log(const char *data, size_t len)
    {
      std::unique_lock<std::mutex> lock(_mutex); // 对共享资源进行加锁，确保线程安全。
      if (_sinks.empty())
        return;
      for (auto &sink : _sinks)
      {
        sink->log(data, len);
      }
    }
  };
  // 异步日志器，用了双缓冲区，交换缓冲区的方式来减少生产者和消费者的锁冲突。同时是否安全的扩容还是无线扩容。lopper_type
  class AsyncLogger : public Logger
  {
  public:
    AsyncLogger(const std::string &logger_name,
                LogLevel::value level,
                Formater::ptr &formater,
                std::vector<LogSink::ptr> &sinks, AsyncType lopper_type)
        : Logger(logger_name, level, formater, sinks), // 绑定一个回调函数
          _lopper(std::make_shared<AsyncLooper>(std::bind(&AsyncLogger::realLog, this, std::placeholders::_1), lopper_type))
    {
    }
    void log(const char *data, size_t len)
    {
      _lopper->push(data, len); // 将数据发送给缓冲区
    }
    void realLog(Buffer &buf) // 这就是回调函数，通过回调函数，就可以实现工作线程的打印工作。
    {
      if (_sinks.empty())
        return;
      for (auto &sink : _sinks)
      {
        sink->log(buf.begin(), buf.readAbleSize());
      }
    }

  private:
    AsyncLooper::ptr _lopper;
  };
  enum class LoggerType
  {
    LOGGER_SYNC,
    LOGGER_ASYNC
  };
  // 使用建造者模式来建造日志器，而不是让用户直接去构造日志器，简化用户的使用复杂度
  // 1.抽象出一个日志器建造者类
  // 1.设置日志器类型
  // 2.将不同类型日志器的创建放到同一个日志器建造者类中完成。
  class LoggerBuilder
  {
  public:
    LoggerBuilder() : _logger_type(LoggerType::LOGGER_SYNC),
                      _limit_level(LogLevel::value::DEBUG), _lopper_type(AsyncType::ASYNC_SAFE) {}
    void buildLoggerType(LoggerType type) { _logger_type = type; }
    void buildEnableUnSafeAsync() { _lopper_type == AsyncType::ASYNC_UNSAFE; }
    void buildLoggername(const std::string &name) { _logger_name = name; }
    void buildLoggerlevel(LogLevel::value level) { _limit_level = level; }
    void buildFormater(const std::string &pattern)
    {
      _formatter = std::make_shared<Formater>(pattern);
    }
    template <typename SinkType, typename... Args>
    void buildSink(Args &&...args)
    {
      LogSink::ptr psink = SinkFactory::create<SinkType>(std::forward<Args>(args)...);
      _sinks.push_back(psink);
    }
    virtual Logger::ptr build() = 0;

  protected:
    AsyncType _lopper_type;
    LoggerType _logger_type;
    std::string _logger_name;
    LogLevel::value _limit_level;
    Formater::ptr _formatter;
    std::vector<LogSink::ptr> _sinks;
  };
  // 2.派生出具体的建造者类--局部日志器的建造者&全局的日志器建造者（后面添加了全局单例管理器之后，将日志器添加全局管理）
  class LocalLoggerBuilder : public LoggerBuilder
  {
  public:
    Logger::ptr build() override
    {
      assert(_logger_name.empty() == false); // 必须有日志器名称
      if (_formatter.get() == nullptr)       // 如果没有给格式，需要默认生成一个格式
      {
        _formatter = std::make_shared<Formater>();
      }
      if (_sinks.empty()) // 如果落地为空，则默认向显示器
      {
        buildSink<StdoutSink>();
      }
      if (_logger_type == LoggerType::LOGGER_ASYNC) // 如果是异步，怎么做
      {
        return std::make_shared<AsyncLogger>(_logger_name, _limit_level, _formatter, _sinks, _lopper_type);
      }
      else
      {
        // 否则是同步
        return std::make_shared<SyncLogger>(_logger_name, _limit_level, _formatter, _sinks);
      }
    }
  };
  class LoggerManger
  {
  public:
    static LoggerManger &getInstance() // 单例模式
    {
      // 返回一个静态对象，单例模式
      static LoggerManger eton;
      return eton;
    }
    void addLogger(Logger::ptr &logger) // 添加日志器
    {
      if (hasLogger(logger->getName()))
        return;
      std::unique_lock<std::mutex> _mutex;
      _loggers.insert(std::make_pair(logger->getName(), logger));
    }
    bool hasLogger(const std::string &name)
    {
      std::unique_lock<std::mutex> lock(_mutex);
      auto it = _loggers.find(name);
      if (it == _loggers.end())
      {
        return false;
      }
      return true;
    }
    Logger::ptr getLogger(const std::string &name)
    {
      std::unique_lock<std::mutex> lock(_mutex);
      auto it = _loggers.find(name);
      if (it == _loggers.end())
      {
        return Logger::ptr();
      }
      return it->second;
    }
    Logger::ptr rootLogger()
    {
      return _root_logger; // 下面构造函数的时候建造好了
    }

  private:
    LoggerManger()
    {
      std::unique_ptr<xglog::LoggerBuilder> builder(new xglog::LocalLoggerBuilder());
      builder->buildLoggername("root");
      _root_logger = builder->build(); // 建造一个默认日志器
      _loggers.insert(std::make_pair("root", _root_logger));
    } // 默认构造
  private:
    std::mutex _mutex;
    Logger::ptr _root_logger; // 默认日志器
    std::unordered_map<std::string, Logger::ptr> _loggers;
  };

  // 设计一个全局日志器建造者
  class globalLoggerBuilder : public LoggerBuilder
  {
  public:
    Logger::ptr build() override
    {
      assert(_logger_name.empty() == false); // 必须有日志器名称
      if (_formatter.get() == nullptr)       // 如果没有给格式，需要默认生成一个格式
      {
        _formatter = std::make_shared<Formater>();
      }
      if (_sinks.empty()) // 如果落地为空，则默认向显示器
      {
        buildSink<StdoutSink>();
      }
      Logger::ptr logger;
      if (_logger_type == LoggerType::LOGGER_ASYNC) // 如果是异步，怎么做
      {
        logger = std::make_shared<AsyncLogger>(_logger_name, _limit_level, _formatter, _sinks, _lopper_type);
      }
      else
      {
        // 否则是同步
        logger = std::make_shared<SyncLogger>(_logger_name, _limit_level, _formatter, _sinks);
      }

      LoggerManger::getInstance().addLogger(logger);
      return logger;
    }
  };
}
#endif